package gui;
import java.awt.EventQueue;

import javax.swing.JFrame;
import java.awt.GridLayout;
import javax.swing.JPanel;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FocusTraversalPolicy;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;

import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.Action;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.DropMode;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.RowSorter;
import javax.swing.SwingWorker;
import javax.swing.TransferHandler;

import java.awt.FlowLayout;

import javax.swing.TransferHandler.TransferSupport;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableRowSorter;
import javax.swing.JMenuBar;
import javax.swing.JButton;

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.rmi.server.Operation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.table.AbstractTableModel;

import org.apache.log4j.Logger;
import org.xml.sax.SAXException;


import javax.swing.JMenu;
import javax.swing.JMenuItem;

import javax.swing.JScrollPane;

import logic.Clipboard;
import logic.FileSystemFactory;
import logic.NotExistingFileException;
import logic.UnsupportedProtocol;
import logic.configuration.Configuration;
import logic.configuration.XMLConfigurationLoader;
import logic.file.IDirectory;
import logic.file.IFile;
import logic.operation.AbstractFileOperation;
import logic.operation.AbstractObservableFileOperation;
import logic.operation.AbstractTransferOperation;
import logic.operation.CopyOperation;
import logic.operation.DeleteOperation;
import logic.operation.IFileOperation;
import logic.operation.InvertSelectionOperation;
import logic.operation.PasteOperation;
import logic.operation.PreviewOperation;

import javax.swing.JToolBar;
import javax.xml.parsers.ParserConfigurationException;
import javax.swing.ImageIcon;

//w polu adresu nie są rozróżniane duże/małe litery - to jest cecha windowsa!
//TODO eksternalizacja Stringów
//TODO na penelu można łapać zdarzenie gainedFocus i odpowiednio reagować ustawiając odpowiedni element panelu na aktywny albo robić cokolwiek innego
//TODO każde wykonanie operacji to nowa instancja - w klasie Akcji przechowywać referencję na obiekt Class jaki ma być tworzony
//TODO redefinicja na tabeli tak by zaznaczanie tylko za pomocą INSERT nie klikanie myszą
//TODO ??? dp implementacji działanie klawisza INSERT takiej jak w TC należałoby rozszerzyć tabelę tak by przechowywała 2 rodzaje zaznaczenie (niejako focus + selected) i wtedy insert by zaznaczał/odznaczał i przechodził dalej z focusem
//TODO Observable
//TODO przy kopiowaniu sprawdzanie canRead i ew. rzucanie wyjątku
//TODO tables current ustawiane też na starcie
//configurationLoader z fabryki
//TODO uporządkować dodawanie listenerów i tworzenie layoutu
//TODO więcej informacji do paska postępu, jaki plik, możliwość anulowania - swing worker może zostać anulowany przez cancel() jednak dodatkowo konieczne jest cofnięcię zmian, każda operacja musi implementować cancel() i trzymać dziennik zmian jakie są wykonywane na bierząco tylko w przypadku move, copy, compress
//TODO ant deployment + skrypty
//TODO wykorzystanie ServiceLoader do łądowanie modułów
//TODO wyłączenie resizowania w pionie na ProgressDialog
//TODO d&d dodanie SwingWorkera
//TODO View -> Switch Layout (wyswietlanie tabelkowe LUB kafelki - JList z customizowanym rendererem - przełączanie za pomocą setVisible() albo CardLayout, raczej pierwsze rozwiązanie)
//TODO toolbar na JMENU
//TODO pula wątków do operacji wielowątkowych
//TODO za każdym razem przy przechodzeniu do nowego folderu są ustawiane oba komponenty wizualizacji i NIE JEST zmieniane setVisible wtedy
//podpięcie Listenerów zdarzeń do listy w konfiguratorze
//TODO refector showDir przekazywanie parametrów i jest zbędne lub zbędnę są lista i tablica
//TODO IKONKA NA PASKU ZADAŃ
//TODO refactor listFiles niech zwraca tablicę
//ToolTip na pliku w widoku listy
//TODO obecnie sortowanie tabeli następuje dwukrotnie - raz 'widoku' i raz modelu - 'wyłączyć sortowanie widoku
//TODO atrybuty

public class MyTC {
	
	static Logger logger = Logger.getLogger(MyTC.class.getName());

	private static MyTC instance;
	private StartupSettingsManager startupSettingsManager = new StartupSettingsManager();

	private JFrame frame;

	private JSplitPane splitPane;
	private JPanel panel;		//główny
	
	private JToolBar toolBar;
	
	JScrollPane [] scrollPanes = new JScrollPane[2];
	private JTable [] tables = new JTable[2];
	@SuppressWarnings("unchecked")
	private JList<IFile> [] lists = new JList[2];
	private JTextField [] addrTextFields = new JTextField[2];
	private JMenu fileMenu;
	private JMenu editMenu;

	private JMenu viewMenu;
	private JMenu helpMenu;
	
	private JTable focusedTable;
	
	JButton [] btnGo = new JButton[2];
	
	private ArrayList<JButton> toolbarButtons = new ArrayList<JButton>();
	
	private MouseListener fileMouseListener = new FileMouseListener();
	private Action enterPressed = new EnterPressedAction();
	private JTextField [] filterTextFields = new JTextField[2];
	//todo wykorzystanie Clipboard z java awt datatransfer???
	private static Clipboard clipboard;
	
	RowFilter<FilesTableModel,Integer> filter;
	private FileRowFilter filter1;
	
	//private TransferHandler transferHandler;
	
	IFile [] tablesCurrent = new IFile[2];

	/**
	 * Create the application.
	 */

	private JPanel [] panels = new JPanel[2];

	private FileListModel[] listModels = new FileListModel[2];

	private MyTC() {
		initialize();
		setTransferHandler(tables[0]);
		//TODO RowFilter.regexFilter()
		//RowFilter.
		TableRowSorter<FilesTableModel> rowSorter = 
				new TableRowSorter<FilesTableModel>(
						(FilesTableModel)tables[0].getModel());
		
		TableRowSorter<FilesTableModel> rowSorter1 = 
				new TableRowSorter<FilesTableModel>(
						(FilesTableModel)tables[1].getModel());
		//TODO
		//dla 100% poprawnego działanie musi zostać przedefiniowane - by brał po uwagę tylko nazwę pliku
		filter = new FileRowFilter();
		filter1 = new FileRowFilter();
		
		rowSorter.setRowFilter(filter);
		rowSorter1.setRowFilter(filter1);
		
		((FileRowFilter)filter).setPattern(filterTextFields[0].getText());
		((FileRowFilter)filter1).setPattern(filterTextFields[1].getText());
		
		tables[0].setRowSorter(rowSorter);
		tables[1].setRowSorter(rowSorter1);
		
		//tables[0].setFocusable(false);
		//tables[1].setFocusable(false);
		
		//TODO NA PANELE i pokombinować tak by focus gained na panelu powodowal aktywacje focusa na tabelę???
		frame.setFocusTraversalPolicy(new CustomFocusTraversalPolicy(tables[0], tables[1]));
		
		//tables[0].setFocusTraversalPolicy(new CustomFocusTraversalPolicy(panels[0], panels[1]));
		//tables[1].setFocusTraversalPolicy(new CustomFocusTraversalPolicy(panels[0], panels[1]));
		//tables[0].setFocusTraversalPolicy(new CustomFocusTraversalPolicy(tables[0], tables[1]));
		
		//TODO żeby przestawiać focus trzeba łapać mouse event z frame albo panela albo jsplitpane i wtedy ustwiać go jako sfocusowany i następnie tabem będzie można panele przełączać - TEST
		panels[0].setFocusable(true);
		panels[0].setRequestFocusEnabled(true);
		
		//TODO ustawić jeszcze default focused component for panels
		tables[0].addFocusListener(new FocusListener() {

			@Override
			public void focusGained(FocusEvent arg0) {
				logger.info("panel 0 gained Focus");
				focusedTable = tables[0];
				//addrTextFields[0].requestFocusInWindow();
			}

			@Override
			public void focusLost(FocusEvent arg0) {
				logger.info("panel 0 lost Focus");
				
			}
			
		});
		
		tables[1].addFocusListener(new FocusListener() {

			@Override
			public void focusGained(FocusEvent arg0) {
				//addrTextFields[1].requestFocusInWindow();
				focusedTable = tables[1];
				logger.info("panel 1 gained Focus");
				
			}

			@Override
			public void focusLost(FocusEvent arg0) {
				logger.info("panel 1 lost Focus");
				
			}
			
		});
		
		tables[0].getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "TabPressed");
		tables[0].getActionMap().put("TabPressed", new TabPressedAction());
		tables[1].getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "TabPressed");
		tables[1].getActionMap().put("TabPressed", new TabPressedAction());

		
		setTransferHandler(tables[1]);
		configure();
		
		frame.addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosed(WindowEvent arg0) {
				
			}

			@Override
			public void windowClosing(WindowEvent arg0) {
				saveSettings();
			}

			@Override
			public void windowOpened(WindowEvent arg0) {
				try {
					if (startupSettingsManager.loadFromFile()) {
						for (int i = 0; i < tables.length; i++) {
							addrTextFields[i].setText(startupSettingsManager.getPath(i));
							showDir(startupSettingsManager.getPath(i), tables[i], lists[i], i);
						}
						//TODO to jest redundantne z addrTextFields
						//TODO ustawienie tablesCurrent
						
					} else {
						//TODO opening default locations
						
					}
				} catch (FileNotFoundException e) {
					logger.warn("Can't find startup settings file");
					for (int i = 0; i < tables.length; i++) {
						addrTextFields[i].setText("./");
						showDir("./", tables[i], lists[i], i);
					}
				} 
				catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
			
		});
		
		
		tables[0].getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), "InsertPressed");
		tables[0].getActionMap().put("InsertPressed", new InsertPressedAction());
		frame.setVisible(true);
	}

	protected void saveSettings() {
		for (int i = 0; i < tablesCurrent.length; i++) {
			startupSettingsManager.setPath(tablesCurrent[i].getPath(), i);
		}
		try {
			startupSettingsManager.saveToFile();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frame = new JFrame();
		frame.setBounds(100, 100, 889, 391);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//frame.getContentPane().setLayout(new GridLayout(1, 2, 0, 0));
		
		drawPanels();
		
		JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panels[0], panels[1]);
		
		GridBagConstraints gbc_split = new GridBagConstraints();
		gbc_split.fill = GridBagConstraints.BOTH;
		gbc_split.insets = new Insets(0, 0, 5, 5);
		gbc_split.gridx = 0;
		gbc_split.gridy = 1;
		panel.add(splitPane, gbc_split);
		
		splitPane.setResizeWeight(0.5);
		//frame.getContentPane().add(splitPane);
		
		JLabel statusLabel = new JLabel("status");
		GridBagConstraints gbc_label = new GridBagConstraints();
		gbc_label.fill = GridBagConstraints.BOTH;
		gbc_label.insets = new Insets(0, 0, 5, 5);
		gbc_label.gridx = 0;
		gbc_label.gridy = 2;
		panel.add(statusLabel, gbc_label);
		
		JMenuBar menuBar = new JMenuBar();
		frame.setJMenuBar(menuBar);
		
		fileMenu = new JMenu("File");
		menuBar.add(fileMenu);
		
		editMenu = new JMenu("Edit");
		menuBar.add(editMenu);
		
		viewMenu = new JMenu("View");
		menuBar.add(viewMenu);
		
		helpMenu = new JMenu("Help");
		menuBar.add(helpMenu);
		
		frame.setVisible(true);
	}
	
	/**
	 * Tworzy elementów menu, które nie są konfigurowalne przez zewnętrzny plik
	 * i dodaje dla nich ActionListenery 
	 */
	private void configureBasicMenuItem() {
		
		JMenuItem fullViewItem = new JMenuItem("Full");
		fullViewItem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				//TODO dot. aktywnego, TODO zmiana ogólnie zamiast ref. na last focused table trzymać referencję na ostatnio panel w którym jest ostatnio sfocusowana tabela
				//TODO by nie powtarzać - wyszukiwanie w tablicach
				if (focusedTable == tables[0]) {
					scrollPanes[0].setViewportView(tables[0]);
					//tables[0].setVisible(false);
					//lists[0].setVisible(true);
				
				} else {
					scrollPanes[1].setViewportView(tables[1]);
					
					//tables[1].setVisible(false);
					//lists[1].setVisible(true);
				
				}
			}

		});
		
		JMenuItem shortViewItem = new JMenuItem("Short");
		shortViewItem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (focusedTable == tables[0]) {
					scrollPanes[0].setViewportView(lists[0]);
					//tables[0].setVisible(false);
					//lists[0].setVisible(true);
				
				} else {
					scrollPanes[1].setViewportView(lists[1]);
					
					//tables[1].setVisible(false);
					//lists[1].setVisible(true);
				
				}
				
			}

		});
		
		getViewMenu().add(fullViewItem);
		getViewMenu().add(shortViewItem);
		
		JMenuItem aboutItem = new JMenuItem("About");
		getHelpMenu().add(aboutItem);
		
		//ustawienie elementów menu, nie będących operacjami
		getFileMenu().addSeparator();
		JMenuItem exitItem = new JMenuItem("Exit");
		exitItem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				saveSettings();
				System.exit(0);
				
			}
			
		});
		getFileMenu().add(exitItem);
		
	}
	
	//TODO ew.rozszerzenie JTable (konieczne ze wzgledu na usuniecie odstepow) i dodanie tej metody do klasy
	//TODO to co znajduje się w tablesCurrent może być na bieżąco wyciągane z table model
	public void showDir(String address, JTable table, JList<IFile> list, int i) {
		try {
			IFile newDir = FileSystemFactory.getElement(address);
			if (newDir instanceof IDirectory) {
				
				tablesCurrent[i] = newDir;
				List<IFile> files = null;
				files = ( (IDirectory) tablesCurrent[i] ).listFiles();
				((FilesTableModel)table.getModel()).setData(files);
									
				((FileListModel) list.getModel()).setData(files);
			} else {
			
				JOptionPane.showMessageDialog(frame, "Error", "Name does not point to a directory" + tablesCurrent[i].getPath(), JOptionPane.ERROR_MESSAGE);
				addrTextFields[i].setText(tablesCurrent[i].getPath());
			
			}
		} catch (UnsupportedProtocol e) {
		
			JOptionPane.showMessageDialog(frame, "Error", "Unsupported protocol" + tablesCurrent[i].getPath(), JOptionPane.ERROR_MESSAGE);
			addrTextFields[i].setText(tablesCurrent[i].getPath());
		} catch (NotExistingFileException e) {
		
			JOptionPane.showMessageDialog(frame, "Error", "Directory does not exist" + tablesCurrent[i].getPath(), JOptionPane.ERROR_MESSAGE);
			addrTextFields[i].setText(tablesCurrent[i].getPath());
		
		}
	}
	
	public void setTransferHandler(final JTable table) {
		table.setDragEnabled(true);
		table.setDropMode(DropMode.ON);
		table.setTransferHandler(new FileTransferHandler());
	}
	
	@SuppressWarnings("serial")
	private class GoToAddressAction extends AbstractAction {
		public void actionPerformed(ActionEvent e) {			//== jest celowe i tak pewnie lepiej zrobic dwie osobne akcje
			String address = null;
			if (e.getSource() == btnGo[0]) {	
				address = addrTextFields[0].getText();
				showDir(address, tables[0], lists[0], 0);
				
			} else if (e.getSource() == btnGo[1]) {
				address = addrTextFields[1].getText();
				showDir(address, tables[1], lists[1], 1);
				
			} else {	
				address = ((JTextField)e.getSource()).getText();
				if (e.getSource() == addrTextFields[0]) {
					showDir(address, tables[0], lists[0], 0);
				} else {
					showDir(address, tables[1], lists[1], 1);
				}
			}
			
		}
	}
	
	//TODO DLA OBU TABEL
	//TODO najlepiej przełączanie po panels a w panelu ustawiony domyślnie focusowany component
	//TODO do tego dorobić listenera dla listy
	private class FileMouseListener extends MouseAdapter {

		@Override
		public void mouseClicked(MouseEvent me) {			//ew. odpalanie innego zdarzenia z tego
			if (me.getClickCount() == 2) {					//TODO refactor
				if (me.getSource() instanceof JTable) {
					doOpenFileAction( (JTable) me.getSource() );
				} else {
					doOpenFileAction( (JList) me.getSource() );
				}
			} 
			
		}		
	}
	
	@SuppressWarnings("serial")
	private class EnterPressedAction extends AbstractAction {

		@Override
		public void actionPerformed(ActionEvent event) {
			doOpenFileAction( (JTable) event.getSource() );
			
		}
		
	}
	
	//TODO REFACTOR TAK BY PRZYJMOWAĆ Component
	private void doOpenFileAction(JTable table) {
			int rowIndex = table.getSelectedRow();
			IFile file = ( (FilesTableModel)table.getModel() ).getRow(rowIndex);
			if (file instanceof IDirectory) {		//wyswietlenie zawartości katalogu w w tabeli
				try {
					List<IFile> files = ( (IDirectory) file ).listFiles();
					( (FilesTableModel) table.getModel() ).setData( ( files ));
					if (table.equals(tables[0])) {
						
						listModels[0].setData(files);
						
						tablesCurrent[0] = file;
						addrTextFields[0].setText(file.getPath());
					} else {
						
						IFile [] filesArr = new IFile[files.size()]; 		//obejście faktu, że nie można rzutować Object [] na X [] - nie ma IS-A pomiędzy tablicami
						files.toArray(filesArr);							//REFACTOR!!!
						listModels[1].setData(files);
						
						tablesCurrent[1] = file;
						addrTextFields[1].setText(file.getPath());
					}
				} catch (UnsupportedProtocol e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} else {								//podglad pliku
				int [] selectedRows = table.getSelectedRows();
				List<IFile> files = new LinkedList<IFile>();
				IFile selectedFile;
				logger.info("Getting selected for deletion");
				for (int row : selectedRows) {
					selectedFile = ((FilesTableModel) table.getModel() ).getRow(row);
					files.add(selectedFile);
				}
				
				
				//TODO Swing Worker
				ExecutorService executor = Executors.newCachedThreadPool();
				executor.execute(new PreviewOperation(files));
			}
			logger.debug(file);
	}
	
	private void doOpenFileAction(JList<IFile> list) {
		IFile file = list.getSelectedValue();				//TODO też wystarczy tylko wydostać źródło zdarzenia i z niego odpowiednio wyciągać
		if (file instanceof IDirectory) {		//wyswietlenie zawartości katalogu na liście
			try {
				List<IFile> files = ( (IDirectory) file ).listFiles();
				( (FileListModel) list.getModel()).setData(files);
				if (list.equals(lists[0])) {			//TODO takie ify są do wywalenia po zrafaktoryzawaniu tablesCurrent do mapy
					( (FilesTableModel) tables[0].getModel() ).setData( files );
					tablesCurrent[0] = file;
					addrTextFields[0].setText(file.getPath());
				} else {
					( (FilesTableModel) tables[1].getModel() ).setData( files );
					tablesCurrent[1] = file;
					addrTextFields[1].setText(file.getPath());
				}
			} catch (UnsupportedProtocol e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {								//podglad pliku
			List<IFile> files = list.getSelectedValuesList();
			//TODO update tablicy też konieczny
			//TODO Swing Worker
			ExecutorService executor = Executors.newCachedThreadPool();
			executor.execute(new PreviewOperation(files));
		}
		logger.debug(file);
}
	
	@SuppressWarnings("serial")
	private class SpacePressedAction extends AbstractAction {

		@Override
		public void actionPerformed(ActionEvent e) {		//TODO SWING WORKER
			//TODO dla zaznaczonych wielu plików
			logger.info(e.getSource());
			int row = ( (JTable) e.getSource() ).getSelectedRow();
			IFile file = ( (FilesTableModel) ( (JTable) e.getSource() ).getModel() ).getRow(row);
			if (file instanceof IDirectory) {
				( (IDirectory) file ).calcDirSize();
				//TODO wywołanie polimorficzne do rozważenia
			}
			//odświerzenie tylko konkretnej komórki/EK TODO tabeli
			((AbstractTableModel)( (JTable) e.getSource() ).getModel())
						.fireTableCellUpdated(row, FilesTableModel.SIZE_COLUMN);
		}
		
	}
	
	
	//TODO
	//do obslugi wszystkich zdarzen wystarczy jedna klasa, podmianie bedzie ulegac jedynie 
	//obiekt operacji w roznych instancjach
	//TODO pewnie można to zrobic pewnie bez osobnych akcji dla kazdej z tabel,
	//zdarzenia można rozróżnić na podstawie danych obiektu ActionEvent
	//mozna tworzyc instancje klasy wewnętrznej na zewnętrz byleby tylko byly one powiązane z instacja klasy
	//zewnętrznej - zapis: np. myTC.new OperationAction() wtedy mozna operation przekazac w konstruktorze
	//TODO takie rozwiązanie - tj. polegające na jednokrotnym tworzeniu obiktu operacji nie jest najlepsze ze względu na 
	//brak możliwości łatwej implementacji undo - lepiej tworzyc nowy obiekt przy każdym wywołaniu a przy konfiguracji zapisywać tutaj tylko obiekt Class<>
	@SuppressWarnings("serial")
	class OperationAction extends AbstractFileOperationAction {
		
		//TODO
		Class<? extends IFileOperation> operationClass;
		
		//AbstractFileOperation operation = new DeleteOperation();					 
		
		public OperationAction(IFileOperation operation) {
			this.operation = operation;
		}

		@Override
		public void actionPerformed(ActionEvent event) {
			//TODO po tym mozna zrobić 
			final JTable table = focusedTable;
			//TODO roznicowanie który panel był ostatnio klikany i z niego pobieranie plikow do operacji
			if (operation instanceof AbstractTransferOperation) {		//po operacjach należy aktualizować oba panele
			
				////////////////////////
				ProgressDialog progressDialog = new ProgressDialog(operation.getName());			//files nie są ustawiane, bo obecnie pliki do wklejenia są pozyskiwane ze schowka
				progressDialog.setVisible(true);
				//TODO current folder musi być ustawiany jako destynacja!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
				////////////////////////////////////////////////////////
				//TODO w przypadku d&d konieczna zmiana
				IFile destinationDir = null;
				if (table.equals(tables[0])) {
					destinationDir = tablesCurrent[0];
				} else {
					destinationDir = tablesCurrent[1];
				}
				((AbstractTransferOperation) operation).setDestination((IDirectory)destinationDir);
				
				((AbstractTransferOperation) operation).registerObserver(progressDialog);
			
			} else if (operation instanceof InvertSelectionOperation) {		//potrzeba jeszcze jednej klasy abstrakcyjnej dla operacji na calej tabeli
				
				((InvertSelectionOperation) operation).setTable(table);							
			
			} else if (operation instanceof AbstractFileOperation) {	

				
				/////////////////////////////////////////////////////////////////////////////
				//pobranie zaznaczonych plikow, TODO enkapsulacja w tabeli najlepiej
				int [] selectedRows = table.getSelectedRows();
				List<IFile> files = new LinkedList<IFile>();
				IFile selectedFile;		//TODO tez do funkcji
				logger.info("Getting selected for deletion");
				for (int row : selectedRows) {
					selectedFile = ((FilesTableModel) table.getModel()).getRow(
												table.convertRowIndexToModel(row));
					
					logger.debug("selected FILE:" + selectedFile);
					
					files.add(selectedFile);
				}
			
				//TODO chyba najelegantszym rozwiazaniem spelniajacym wymagania projektowe oraz
				//pozwalajacym na operacji nie zajmowac sie pozyskiwaniem zaznaczonych plikow jest 
				//parsowanie xmla i zapisywanie rezultatow parsowania do np. mapy.
				//lub podczas parsowanie ustawianie stosownych operacji dla akcji okreslonych buttonow
				//operacje wtedy powinny byc jako skladowe klas akcji, 
				//wtedy ten konstruktor musi byc wywolywany z uzyciem refleksji
				//operation.getClass.getConstructors
				((AbstractFileOperation) operation).setFiles(files);
				if (operation instanceof AbstractObservableFileOperation) {
					ProgressDialog progressFrame = new ProgressDialog(operation.getName());
					progressFrame.setVisible(true);
					((AbstractObservableFileOperation) operation).registerObserver(progressFrame);
				}
			}
			final int tableAffected;
			if (table == tables[1]) {
				tableAffected = 1;
			} else {
				tableAffected = 0;
			}
			
			SwingWorker<Object, Object> worker = new SwingWorker<Object, Object>() {

				@Override
				protected Object doInBackground() throws Exception {
					operation.execute();
					return null;
				}
				
				@Override
				protected void done() {
					try {
						//logger.debug("DUPA");
						//logger.debug(( (IDirectory) tablesCurrent[tableAffected] ).listFiles());
						//logger.debug("DUPA");
						if (operation instanceof AbstractTransferOperation || 
								operation instanceof DeleteOperation) {
							((FilesTableModel) table.getModel()).setData( 
								( (IDirectory) tablesCurrent[tableAffected] ).listFiles() );
						}
					} catch (UnsupportedProtocol e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			};
			
			worker.execute();
			
			
		}		
		
	}
	
	//TODO niech to tez będzie w ramach OperationAction 
	//by byla możliwa jednorodna konfiguracja
	//wtedy w actionPerformed wyifowanie typu działania po instanceof operacji
//	public class SelectionOperationAction extends AbstractAction {
//		
//		IFileOperation operation = new InvertSelectionOperation(leftTable);
//
//		@Override
//		public void actionPerformed(ActionEvent arg0) {
//			
//			ProgressDialog progressFrame = new ProgressDialog();
//			progressFrame.setVisible(true);
//			
//			ExecutorService exs = Executors.newCachedThreadPool();
//			exs.execute(operation);
//			// TODO Auto-generated method stub
//		}
//		
//		public void setOperation(AbstractFileOperation operation) {
//			this.operation = operation;
//			
//		}
//		
//	}
	
	/**
	 * Konfiguruje elementy menu i toolbara
	 */
	private void configure() {
		XMLConfigurationLoader configurationLoader = new XMLConfigurationLoader();		//TODO z fabryki
		Configuration configuration;
		try {
			configuration = configurationLoader.parse(new File("MyTC-config.xml"));
			Configurator configurator = new Configurator(configuration, this);
			try {
				configurator.configure();
				configureBasicMenuItem();
				//configurator.configure(1);		
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//toolbarButtons.get(4).removeActionListener(toolbarButtons.get(4).getActionListeners()[0]);
		//toolbarButtons.get(4).addActionListener(new SelectionOperationAction());
		
	}
	
	List<JButton> getToolbarButtons() {
		return toolbarButtons;
	}
	
	JToolBar getToolBar() {
		return toolBar;
	}
	
	JMenu getFileMenu() {
		return fileMenu;
	}
	
	JMenu getEditMenu() {
		return editMenu;
	}

	public JTable getTable(int i) {
		return tables[i];
	}
	
	/**
	 * @return the viewMenu
	 */
	public JMenu getViewMenu() {
		return viewMenu;
	}

	/**
	 * @return the helpMenu
	 */
	public JMenu getHelpMenu() {
		return helpMenu;
	}

	public static MyTC getInstance() {
		if (instance == null) {
			instance = new MyTC();
		}
		return instance;
	}
	
	//TODO do klasy ClipBoard
	public static Clipboard getClipboard() {
		if (clipboard == null) {
			clipboard = new Clipboard();
		}
		return clipboard;
	}

	public void saveToClipboard(List<IFile> files) {
		MyTC.getClipboard().save(files);
		
	}
	
	@SuppressWarnings("serial")
	private class FileTransferHandler extends TransferHandler {
		
		@Override
		public int getSourceActions(JComponent c) {
			//to odpowiedni przycisk myszy (ew. użycie controla) określa typ akcji???
			logger.info("list getSource executed");
		    return COPY_OR_MOVE;
		}

		@Override
		protected Transferable createTransferable(JComponent c) {
			logger.info("list createTr executed" + c);
			//TODO w normalnym programie mozna tutaj przesłać source component z którego pochodzi drag
			//możnaby ew. przesyłać ścieżke
			if (c.equals(tables[0])) {
				return new StringSelection("left");
			} else {
				return new StringSelection("right");
			}
		}
		
		@Override
		protected void exportDone(JComponent c, Transferable t, int action) {
			logger.info("list exportD executed");
		    if (action == MOVE) {
		        //c.removeSelection();
		    }
		}
		
		@Override
		public boolean canImport(TransferSupport info) {
			logger.info("list canImport executed");
			return true;
		}
		
		//wywoływane w obiekcie na którym następuje drop
		@Override
		public boolean importData(TransferSupport info) {
			logger.info("list importData executed");
			//logger.info(table.getSelectedRow());
			
			try {
				if (info.getTransferable().getTransferData(DataFlavor.stringFlavor).equals("left")) {
					//kopiujemy z lewego
					
					int [] selectedRows = tables[0].getSelectedRows();
					List<IFile> files = new LinkedList<IFile>();
					IFile selectedFile;		//TODO tez do funkcji
					logger.info("Getting selected for deletion");
					for (int row : selectedRows) {
						selectedFile = ((FilesTableModel)tables[0].getModel()).getRow(row);
						files.add(selectedFile);
					}
					//D&D odbywa się bez udziału schowka
					//TODO lepiej wykorzystać cała ActionOperation
					//do każdej operacji powienien raczej być przekazywany cały obiekt ProgressDialog
					int target = ((JTable)info.getComponent()).rowAtPoint(info.getDropLocation().getDropPoint());
					IFile targetFile = ( (FilesTableModel) ((JTable)info.getComponent()).getModel()).getRow(target);
					//TODO rozbicie na Copy i Paste
					if (!(targetFile instanceof IDirectory)) {
						//TODO wtedy kopiowanie do biezacego folderu z danej tabeli
					}
					IFileOperation operation = new PasteOperation();
					( (AbstractTransferOperation) operation).setFiles(files);
					( (AbstractTransferOperation) operation).setDestination((IDirectory)targetFile);
					( (AbstractTransferOperation) operation).registerObserver(new ProgressDialog(operation.getName()));
					//ręczne wywołanie akcji
					ExecutorService executor = Executors.newCachedThreadPool();		//SwingWorker
					executor.execute(operation);
					
				} else {
					//kopiujemy z prawego
					
					int [] selectedRows = tables[1].getSelectedRows();
					List<IFile> files = new LinkedList<IFile>();
					IFile selectedFile;		//TODO tez do funkcji
					logger.info("Getting selected for deletion");
					for (int row : selectedRows) {
						selectedFile = ((FilesTableModel)tables[1].getModel()).getRow(row);
						files.add(selectedFile);
					}
					
					IFileOperation operation = new CopyOperation();
					//ręczne wywołanie akcji
					ExecutorService executor = Executors.newCachedThreadPool();		//SwingWorker
					executor.execute(operation);
				}
			} catch (UnsupportedFlavorException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			try {
				logger.debug(info.getTransferable().getTransferData(DataFlavor.stringFlavor));
			} catch (UnsupportedFlavorException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
			
		}
		
	}
	
	public void drawPanels() {
		
		
		//panel główny
		panel = new JPanel();
		GridBagLayout gbl_panelMain = new GridBagLayout();
		gbl_panelMain.columnWidths = new int[]{790};
		gbl_panelMain.rowHeights = new int[]{20, 20+27+321, 8};
		gbl_panelMain.columnWeights = new double[]{1.0, 1.0};
		gbl_panelMain.rowWeights = new double[]{0.0, 1.0, 0.0};			//waga 0.0 blokuje resizing
		panel.setLayout(gbl_panelMain);
		frame.getContentPane().add(panel);
		
		toolBar = new JToolBar();
		toolBar.setFloatable(false);
		//toolBar.setMinimumSize(new Dimension(20,100));
		//toolBar.setMaximumSize(new Dimension(20,100));
		
		
		GridBagConstraints gbc_tool = new GridBagConstraints();
		gbc_tool.fill = GridBagConstraints.BOTH;
		gbc_tool.insets = new Insets(1, 1, 1, 1);
		gbc_tool.gridx = 0;
		gbc_tool.gridy = 0;
		gbc_tool.weighty = 0.0;
		panel.add(toolBar, gbc_tool);
		
		for (int i = 0; i < 2; i++) {
			
			//dwa panele wewnętrzne
			panels[i] = new JPanel();
			//TODO jeszcze jeden panel zawierający dwa panele
			
			//GridBagConstraints gbc_panel = new GridBagConstraints();
			//gbc_panel.fill = GridBagConstraints.BOTH;
			//gbc_panel.insets = new Insets(0, 0, 5, 5);
			//gbc_panel.gridx = i;
			//gbc_panel.gridy = 1;
			//panel.add(panels[i], gbc_panel);
			
			GridBagLayout gbl_panel = new GridBagLayout();
			gbl_panel.columnWidths = new int[]{395};
			gbl_panel.rowHeights = new int[]{27, 321};
			gbl_panel.columnWeights = new double[]{1.0};
			gbl_panel.rowWeights = new double[]{0.0, 1.0};
			panels[i].setLayout(gbl_panel);
			
			//JPanel btnpanel = new JPanel();
			//GridBagConstraints gbc_btnpanel = new GridBagConstraints();
			//gbc_btnpanel.fill = GridBagConstraints.BOTH;
			//gbc_btnpanel.insets = new Insets(0, 0, 5, 5);
			//gbc_btnpanel.gridx = 0;
			//gbc_btnpanel.gridy = 0;
			//panels[i].add(btnpanel, gbc_btnpanel);
			//btnpanel.setLayout(new GridLayout(0, 1, 0, 0));
						
			JPanel addrpanel = new JPanel();
			GridBagConstraints gbc_addrpanel = new GridBagConstraints();
			gbc_addrpanel.fill = GridBagConstraints.BOTH;
			gbc_addrpanel.insets = new Insets(1, 1, 1, 1);
			gbc_addrpanel.gridx = 0;
			gbc_addrpanel.gridy = 0;
			panels[i].add(addrpanel, gbc_addrpanel);
			GridBagLayout gbl_addrpanel = new GridBagLayout();
			gbl_addrpanel.columnWidths = new int[]{200, 33, 30};
			gbl_addrpanel.rowHeights = new int[]{22};
			gbl_addrpanel.columnWeights = new double[]{1.0, 0.1, 0.0};
			gbl_addrpanel.rowWeights = new double[]{0.0};
			addrpanel.setLayout(gbl_addrpanel);
			
			//String []  strT = {"1","2","3"};
			//JComboBox diskList = new JComboBox(strT );
			//addrTextFLeft.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke("ENTER"), "EnterPressed");
			//addrTextFLeft.getActionMap().put("EnterPressed", goToAddressActionLeft);
			//diskList.addMouseListener(null);
			//GridBagConstraints gbc_diskList = new GridBagConstraints();
			//gbc_diskList.insets = new Insets(0, 0, 0, 5);
			//gbc_diskList.fill = GridBagConstraints.BOTH;
			//gbc_diskList.gridx = 0;
			//gbc_diskList.gridy = 0;
			//addrpanel.add(diskList, gbc_diskList);
			
			addrTextFields[i] = new JTextField();
			//addrTextFLeft.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke("ENTER"), "EnterPressed");
			//addrTextFLeft.getActionMap().put("EnterPressed", goToAddressActionLeft);
			addrTextFields[i].addActionListener(new GoToAddressAction());
			GridBagConstraints gbc_addrTextFLeft = new GridBagConstraints();
			gbc_addrTextFLeft.insets = new Insets(1, 1, 1, 1);
			gbc_addrTextFLeft.fill = GridBagConstraints.BOTH;
			gbc_addrTextFLeft.gridx = 0;
			gbc_addrTextFLeft.gridy = 0;
			addrpanel.add(addrTextFields[i], gbc_addrTextFLeft);
			
			filterTextFields[i] = new JTextField();
			filterTextFields[i].setText("*.*");
			//ew. TODO natychmiastowej filtracji należałoby użyć DocumentListera albo ChangeListener
			filterTextFields[i].getDocument().addDocumentListener(new FilterFile(i));

			GridBagConstraints gbc_textField = new GridBagConstraints();
			gbc_textField.insets = new Insets(1, 1, 1, 1);
			gbc_textField.fill = GridBagConstraints.BOTH;
			gbc_textField.gridx = 1;
			gbc_textField.gridy = 0;
			addrpanel.add(filterTextFields[i], gbc_textField);
			
			btnGo[i] = new JButton("GO");
			btnGo[i].addActionListener(new GoToAddressAction());
			GridBagConstraints gbc_btnGo = new GridBagConstraints();
			gbc_btnGo.insets = new Insets(1, 1, 1, 1);
			gbc_btnGo.gridx = 2;
			gbc_btnGo.gridy = 0;
			addrpanel.add(btnGo[i], gbc_btnGo);
			
			scrollPanes[i] = new JScrollPane();
			GridBagConstraints gbc_scrollPane = new GridBagConstraints();
			gbc_scrollPane.fill = GridBagConstraints.BOTH;
			gbc_scrollPane.insets = new Insets(1, 1, 1, 1);
			gbc_scrollPane.gridx = 0;
			gbc_scrollPane.gridy = 1;
			panels[i].add(scrollPanes[i], gbc_scrollPane);
			
			tables[i] = new JTable(new FilesTableModel(null, new String[]{"Nazwa", "Typ", "Wielkość", "Data modyfikacji", "Atrybuty"}));
			tables[i].getColumn("Nazwa").setCellRenderer(new FileNameCellRenderer());
			tables[i].getColumn("Data modyfikacji").setCellRenderer(new FileDateCellRenderer());
			tables[i].setShowGrid(false);
			tables[i].addMouseListener(fileMouseListener);
			
			tables[i].getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "EnterPressed");
			tables[i].getActionMap().put("EnterPressed", enterPressed);
			//przeliczanie rozmiaru katalogu
			tables[i].getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "SpacePressed");
			tables[i].getActionMap().put("SpacePressed", new SpacePressedAction());
			scrollPanes[i].setViewportView(tables[i]);
			
			tables[i].getParent().setBackground(Color.WHITE);
			
			//do innej funkcji odpowiwadającej za inicjalizację ligiki
			listModels[i] = new FileListModel();
			//TODO
			lists[i] = new JList<IFile>(listModels[i]);
			lists[i].setCellRenderer(new FileListCellRenderer());
			
			lists[i].getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "EnterPressed");
			lists[i].getActionMap().put("EnterPressed", enterPressed);
			//przeliczanie rozmiaru katalogu
			lists[i].getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "SpacePressed");
			lists[i].getActionMap().put("SpacePressed", new SpacePressedAction());
			
			//??? scrollPane.setViewportView(tables[i]);
			//tables[i].getParent().setOpaque(true);
			
			lists[i].setVisibleRowCount(0);
			lists[i].setLayoutOrientation(JList.HORIZONTAL_WRAP);
			
			lists[i].setAlignmentX(JList.CENTER_ALIGNMENT);
			
			lists[i].setFixedCellHeight(60);
			lists[i].setFixedCellWidth(45);
			
			lists[i].setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
			
			lists[i].addMouseListener(fileMouseListener);
			
			lists[i].addListSelectionListener(new ListSelectionListener() {

				@Override
				public void valueChanged(ListSelectionEvent event) {
					//zmiana selection w tablicy też
					List<IFile> selectedFiles = ( (JList<IFile>) event.getSource()).getSelectedValuesList();
					if (event.getSource() == lists[0]) {			//TODO REFACTOR do funkcji
						//tables[0].getSelectionModel().add			//TODO customizacja tabeli i dodanie koniecznej metody do zaznaczania na podstawie IFile albo zaznaczanie po indeksach ale wtedy trzeba zachować to samo sortowanie w tabeli i liście
																	//raczej to drugie, żeby zachować posortowanie
					}												//najlepiej niech klikanie po nagłówku tabeli oznacza wywołanie zdarzenia, które posortuje dane z modelu odpowiednio, w liście jest ta sama lista więc wywołujac tylko odpowiednie fire... wystarczy 
				}
				
			});
			
			//tabela nie ma możliwości dodania ListSelectionListenera, w ten sposób aktualizacja selection w tabeli po zmianie selection w Liście nie wywoła niekończonego łańcucha propagacji zdarzeń
			tables[i].getTableHeader().addMouseListener(new MouseAdapter() {
				
				Map<String, Integer> columnsClicks = new HashMap<String, Integer>();
				@Override
				public void mouseClicked(MouseEvent me) {
					//todo wykrywanie kolumny i sortowanie modelu po tej kolumnie
					logger.info("Header clicked");
					TableColumnModel tcm = tables[0].getColumnModel();
					int colIndex = tcm.getColumnIndexAtX((int) me.getPoint().getX());
					colIndex = tables[0].convertColumnIndexToModel(colIndex);
					TableColumn tc = tcm.getColumn(colIndex);
					logger.info(tc.getIdentifier());
					SortDirection sortDirection = null;
					Comparator<IFile> comparator = null;
					//tables[i].getTableHeader().getLme.getPoint();
					Integer currentClicks = columnsClicks.get(tc.getIdentifier());
					currentClicks = ( currentClicks == null ? 0 : currentClicks );
					columnsClicks.put((String) tc.getIdentifier(), currentClicks + 1);
					
					if (columnsClicks.get(tc.getIdentifier()) % 1 == 0) {
						sortDirection = SortDirection.DESC;
					} else {
						sortDirection = SortDirection.ASC;
					}
					if (tc.getIdentifier().equals("Nazwa")) {
						//sort Model by nazwa
						comparator = new FileNameComparator();
							
					} else if (tc.getIdentifier().equals("Typ")) {
						
						comparator = new FileTypeComparator();
						
					} else if (tc.getIdentifier().equals("Wielkość")) {
						
						comparator = new FileSizeComparator();
						
					} else if (tc.getIdentifier().equals("Data modyfikacji")) {
						
						comparator = new FileAttributesComparator();
						
					} else if (tc.getIdentifier().equals("Atrybuty")) {
						
						comparator = new FileDateComparator();
						
					}//sortowanie widoku tabeli powoduje posortowanie danych w modelu
					
								//wyczyszczenie listy by uniknąć niezdefiniowanego zachowania wynikającego z modyfikacja vectora ustawionego przez JList.setData(...)

					
					//lists[0].invalidate();		//chyba jednak trzeba przez model								
					( (FilesTableModel) tables[0].getModel()).sort(comparator, sortDirection);
					( (FilesTableModel) tables[0].getModel()).fireTableDataChanged();
					//lists[0].setListData(( (IDirectory) tablesCurrent[0]).listFiles());		//tyLKo testowo!!!
					//((AbstractListModel<IFile>) lists[0].getModel() ).fireContentsChanged(lists[0].getModel(), 0, lists[0].getLastVisibleIndex());		
					//w odziedziczonej
					List<IFile> files = ( (FilesTableModel) tables[0].getModel()).getRows();
					listModels[0].setData(files);
					
					//lists[0].invalidate();	
					
					
					//TODO najlepiej własny model listy, chociaż nie ma potrzeby dodawanie metod sortujących w modelu listy, ty
				}
			});
			
		}
	}
	
	private class FilterFile implements DocumentListener {
		
		int i;
		FilterFile(int i) {
			this.i = i;
		}
		
		@Override
		public void changedUpdate(DocumentEvent arg0) {
			logger.info("filterTextFiled executed");
			//TODO problem: przy nie zgadza sie ikona po filtrowaniu np. uzycie *.txt - powód: indeksy modelu nie zostają "zaaktualizowane"
			//convertRowIndex...
			((FileRowFilter)filter).setPattern(filterTextFields[i].getText());
			//leftTable.con
			((AbstractTableModel) tables[i].getModel()).fireTableDataChanged();
			
		}

		@Override
		public void insertUpdate(DocumentEvent arg0) {
			logger.info("filterTextFiled executed");
			//TODO problem: przy nie zgadza sie ikona po filtrowaniu np. uzycie *.txt - powód: indeksy modelu nie zostają "zaaktualizowane"
			//convertRowIndex...
			((FileRowFilter)filter).setPattern(filterTextFields[i].getText());
			//leftTable.con
			((AbstractTableModel) tables[i].getModel()).fireTableDataChanged();
			
		}

		@Override
		public void removeUpdate(DocumentEvent arg0) {
			logger.info("filterTextFiled executed");
			//TODO problem: przy nie zgadza sie ikona po filtrowaniu np. uzycie *.txt - powód: indeksy modelu nie zostają "zaaktualizowane"
			//convertRowIndex...
			((FileRowFilter)filter).setPattern(filterTextFields[i].getText());
			//leftTable.con
			((AbstractTableModel) tables[i].getModel()).fireTableDataChanged();
		}
		
	}
	
	//TODO utworzenie osobny
	//TODO żeby na tabelce była odpowiednia reakcja na taba można spróbować dodać Keylistener albo lepiej spr. powiązać akcję z tabem
	//normalnie ctrl+tab wychodzi z tabeli - zrobić akcje i powiązać ją z tabem
	private class CustomFocusTraversalPolicy extends FocusTraversalPolicy {
		
		List<Component> components; 	
		
		CustomFocusTraversalPolicy(Component... components) {
			this.components = Arrays.asList(components);
		}
		
		@Override
		public Component getComponentAfter(Container container, Component component) {
			logger.info("Get Component After executed");
			int idx = ( components.indexOf(component) + 1 ) % components.size();
			return components.get(idx);
		}

		@Override
		public Component getComponentBefore(Container arg0, Component arg1) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public Component getDefaultComponent(Container arg0) {
			return components.get(0);
		}

		@Override
		public Component getFirstComponent(Container arg0) {
			return components.get(0);
		}

		@Override
		public Component getLastComponent(Container arg0) {
			return components.get(components.size()-1);
		}
		
	}
	
	//By można było używać tabulatora na tabeli do przechodzenia do następnego panelu/tabeli 
	@SuppressWarnings("serial")
	private class TabPressedAction extends AbstractAction {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
			
		}
	}
	
	@SuppressWarnings("serial")
	private class InsertPressedAction extends AbstractAction {
		
		// TODO to powinno się raczej znaleźć w tabeli, lewy nie powinien zaznaczać a tylko spełniać rolę "wskaźnika"
		int position = 0;

		@Override
		public void actionPerformed(ActionEvent event) {
			JTable table = (JTable) event.getSource();
			logger.info(table.getSelectionModel().getLeadSelectionIndex());
			( (FilesTableModel) table.getModel()).getRow(position);
			
			if (table.getSelectionModel().isSelectedIndex(position)) { 
				table.getSelectionModel().removeSelectionInterval(position, position);
			} else {
				table.getSelectionModel().addSelectionInterval(position, position);
			}
			//raczej ciekawostka
			position = position < table.getRowCount()-1 ? position+1 : position ;
		}
	}

	public JList<IFile> getList(int i) {
		return lists[i];
	}
	

}
